package com.google.appengine.a4ieve.servlets;

import com.google.appengine.a4ieve.Const;
import com.google.appengine.a4ieve.Helper;
import com.google.appengine.a4ieve.entities.CuratorWrapper;
import com.google.appengine.a4ieve.servlets.resources.Servant;
import com.google.appengine.a4ieve.servlets.resources.ServletProvidable;
import com.google.appengine.api.datastore.*;
import com.google.appengine.api.users.UserServiceFactory;
import org.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ServiceLoader;
import java.util.logging.Logger;

/**
 * User: lunin
 * Date: 4/22/14
 * Time: 2:41 PM
 */
public class FrontControllerServlet extends HttpServlet {

    private static final ServiceLoader<ServletProvidable> LOADER = ServiceLoader.load(ServletProvidable.class);
    private static final String COMMAND_KEY = "command";
    private static final Logger LOG = Logger.getLogger(FrontControllerServlet.class.getName());
    private static final String JSON_CONTENT = "application/json";

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            String commandKey = req.getParameter(COMMAND_KEY);
            if (req.getContentType().contains(JSON_CONTENT)) {
                JSONObject inputJson = Helper.parseJsonFromRequest(req);
                if (inputJson.has(COMMAND_KEY)) {
                    commandKey = (String) inputJson.get(COMMAND_KEY);
                }
            }
            //check for group attribute
            String groupId = (String) req.getSession().getAttribute(Const.GROUP);
            if (groupId == null) {
                Query q = new Query(CuratorWrapper.KIND_KEY).addFilter(
                        CuratorWrapper.USER_ID_PROPERTY,
                        Query.FilterOperator.EQUAL,
                        Helper.cropEmailId(UserServiceFactory.getUserService().getCurrentUser().getEmail())
                );
                DatastoreService service = DatastoreServiceFactory.getDatastoreService();
                Entity foundEntity = service.prepare(q).asSingleEntity();
                String foundGroupId = (String)service.get(foundEntity.getKey()).getProperty(CuratorWrapper.GROUP_KEY_PROPERTY);
                LOG.info("Setting group id to " + foundGroupId);
                req.getSession().setAttribute(Const.GROUP, foundGroupId == null ? "" : foundGroupId);
            }
            Servant command = getCommand(commandKey);
            command.process(req, resp);
        } catch (EntityNotFoundException e) {
            LOG.severe("Entity not found in database");
        }
    }

    protected Servant getCommand(String key) {
        LOG.severe("Getting command for key " + key);
        for (ServletProvidable provider : LOADER) {
            LOG.severe("Starting command lookup");
            Servant command = null;
            try {
                Class commandClass = provider.getClasses().get(key);
                Constructor commandConstructor = commandClass.getDeclaredConstructor();
                commandConstructor.setAccessible(true);
                command = (Servant) commandConstructor.newInstance();
            } catch (InstantiationException e) {
                LOG.severe("Error while creating servant instance via reflection " + e);
            } catch (IllegalAccessException e) {
                LOG.severe("Error while creating servant instance via reflection " + e);
            } catch (NoSuchMethodException e) {
                LOG.severe("Error while creating servant instance via reflection " + e);
            } catch (InvocationTargetException e) {
                LOG.severe("Error while creating servant instance via reflection " + e);
            }
            if (command != null) {
                return command;
            }
        }

        return null;
    }
}
